#include "e.h"
#include "e_mod_main.h"
#include "compiz.h"
#include "bang.h"
#include "bang_window.h"

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/shape.h>

//# define D(x)  do {printf(__FILE__ ":%d:  ", __LINE__); printf x; fflush(stdout);} while (0)
# define D(x)  ((void) 0)

//# define DN(x)  do {printf(__FILE__ ":%d:  ", __LINE__); printf x; fflush(stdout);} while (0)
# define DN(x)  ((void) 0)

static char *displayPrivateIndices = 0;
static int  displayPrivateLen = 0;

static int
reallocDisplayPrivate (int  size,
                       void *closure)
{
D(("BEGIN\n"));
   CompDisplay *d = bang->displays->data;
   void        *privates;

   if (d)
   {
      privates = realloc (d->privates, size * sizeof (CompPrivate));
      if (!privates)
         return FALSE;

      d->privates = (CompPrivate *) privates;
   }

   return TRUE;
}

int
allocateDisplayPrivateIndex (void)
{
   D(("BEGIN\n"));
   return allocatePrivateIndex (&displayPrivateLen,
                                &displayPrivateIndices,
                                reallocDisplayPrivate,
                                0);
}

void
freeDisplayPrivateIndex (int index)
{
   D(("BEGIN\n"));
   freePrivateIndex (displayPrivateLen, displayPrivateIndices, index);
}


static Bool
initPluginForDisplay (CompPlugin  *p,
                      CompDisplay *d)
{
   D(("BEGIN\n"));
    return (*p->vTable->initDisplay) (p, d);
}

static void
finiPluginForDisplay (CompPlugin  *p,
                      CompDisplay *d)
{
   DF(("finiPluginForDisplay [name: %s]\n",p->vTable->name));
   if (p->vTable->finiDisplay)
      (*p->vTable->finiDisplay) (p, d);
}


static int errors = 0;

void errorHandler(void *data)
{
   printf(("!!!!! errorHandler !!!!!!\n"));
}
static int
errorHandlerX (Display     *dpy,
              XErrorEvent *e)
{
   D(("!!!!! errorHandler !!!!!!\n"));

#if DEBUG
   char str[128];
   char *name = 0;
   int  o;
#endif

   errors++;

#if DEBUG
   if (!bang) return 0;
   if (!bang->displays) return 0;
   
   XGetErrorDatabaseText (dpy, "XlibMessage", "XError", "", str, 128);
   fprintf (stderr, "%s", str);

   CompDisplay *d = bang->displays->data;
   o = e->error_code - d->damageError;
   switch (o) {
      case BadDamage:
         name = "BadDamage";
      break;
      default:
         break;
   }
   
   if (name)
   {
      fprintf (stderr, ": %s\n  ", name);
   }
   else
   {
      XGetErrorText (dpy, e->error_code, str, 128);
      fprintf (stderr, ": %s\n  ", str);
   }

   XGetErrorDatabaseText (dpy, "XlibMessage", "MajorCode", "%d", str, 128);
   fprintf (stderr, str, e->request_code);
   
   sprintf (str, "%d", e->request_code);
   XGetErrorDatabaseText (dpy, "XRequest", str, "", str, 128);
   if (strcmp (str, ""))
      fprintf (stderr, " (%s)", str);
   fprintf (stderr, "\n  ");
   
   XGetErrorDatabaseText (dpy, "XlibMessage", "MinorCode", "%d", str, 128);
   fprintf (stderr, str, e->minor_code);
   fprintf (stderr, "\n  ");
   
   XGetErrorDatabaseText (dpy, "XlibMessage", "ResourceID", "%d", str, 128);
   fprintf (stderr, str, e->resourceid);
   fprintf (stderr, "\n");

#endif

   return 0;
}

void
addScreenToDisplay (CompDisplay *display,
                    CompScreen  *s)
{
   display->screens = eina_list_append(display->screens, s);
}

Bool
addDisplay(void)
{
D(("-------------------------- BEGIN addDisplay -----------------------------------\n"));
   CompDisplay *d;
   Display     *dpy;
   int         compositeMajor, compositeMinor;

   d = E_NEW(CompDisplay, 1);
   if (!d) return FALSE;
   
   if (displayPrivateLen)
   {
      d->privates = malloc (displayPrivateLen * sizeof (CompPrivate));
         if (!d->privates)
            return FALSE;
   }
   else
      d->privates = 0;

   d->screenPrivateIndices = 0;
   d->screenPrivateLen     = 0;

   d->textureFilter = GL_LINEAR;

   d->display = dpy = ecore_x_display_get();


#if DEBUG
   XSynchronize (dpy, TRUE);
   //XSetErrorHandler (errorHandler); //TODO DAVE reenable and find a way to unset
   ecore_x_error_handler_set(errorHandler, NULL);
#endif

   d->initPluginForDisplay = initPluginForDisplay;
   d->finiPluginForDisplay = finiPluginForDisplay;


   d->workareaAtom = XInternAtom (dpy, "_NET_WORKAREA", 0);

   if (!XQueryExtension (dpy,
                         COMPOSITE_NAME,
                         &d->compositeOpcode,
                         &d->compositeEvent,
                         &d->compositeError))
   {
      _berror("Your X server does not have the Composite extension.");
      return FALSE;
   }

   XCompositeQueryVersion (dpy, &compositeMajor, &compositeMinor);
   if (compositeMajor == 0 && compositeMinor < 2)
   {
      _berror("Your X server have an old Composite extension (v0.3 required).");
      return FALSE;
   }

   if (!XDamageQueryExtension (dpy, &d->damageEvent, &d->damageError))
   {
      _berror("Your X server does not have the Damage extension.");
      return FALSE;
   }

   if (!XRRQueryExtension (dpy, &d->randrEvent, &d->randrError))
   {
      _berror("Your X server does not have the RandR extension.");
      return FALSE;
   }

   if (!XSyncQueryExtension (dpy, &d->syncEvent, &d->syncError))
   {
      _berror("Your X server does not have the Sync extension.");
      return FALSE;
   }

   d->shapeExtension = XShapeQueryExtension (dpy,
                        &d->shapeEvent,
                        &d->shapeError);


   bang->displays = eina_list_append(bang->displays, d);

D(("-------------------------- END   addDisplay -----------------------------------\n"));
    return TRUE;
}


void 
removeDisplay(CompDisplay *d)
{
   /* Remove all screen from display */
   while (d->screens)
   {
      CompScreen *s = d->screens->data;
      removeScreen(s);
   }
 
   DF(("FREE DISPLAY %d\n", d->screenPrivateLen));
   DF(("  screenPrivateLen: %d\n", d->screenPrivateLen));
   
   bang->displays = eina_list_remove(bang->displays, d);
   
   /* Free display */

/*  TODO FREE this:?
   char *screenPrivateIndices;
   int  screenPrivateLen;
   CompPrivate *privates;
  */ 
}

CompScreen *
findScreenAtDisplay (CompDisplay *d,
                     Window      root)
{
DN(("BEGIN\n"));
   CompScreen *s;
   Eina_List *l;

   for (l = d->screens;l ;l = l->next)
   {
      s = l->data;
      if (s->root == root)
      {
         D(("findScreenAtDisplay: FOUND SCREEN\n"));	
         return s;
      }
   }
   D(("findScreenAtDisplay: SCREEN NOT FOUND\n"));
   return 0;
}


void
forEachWindowOnDisplay (CompDisplay	     *display,
                        ForEachWindowProc proc,
                        void             *closure)
{
D(("BEGIN\n"));
   CompScreen *s;
   Eina_List *l;
   
   for (l = display->screens;l ;l = l->next)
      forEachWindowOnScreen ((CompScreen*)l->data, proc, closure);
}

CompWindow *
findWindowAtDisplay (CompDisplay *d,
                     Window      id)
{
DN((("findWindowAtDisplay (CompDisplay *d, Window id) id: %x\n"),id));
   if (lastFoundWindow && lastFoundWindow->id == id)
   {
      return lastFoundWindow;
   }
   else
   {
      CompScreen *s;
      CompWindow *w;
      Eina_List *l;
      for (l = d->screens ;l ;l = l->next)
      {
         s = l->data;
         w = findWindowAtScreen (s, id);
         if (w)
            return w;
      }
   }

   return 0;
}

